﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;

namespace JControlEx.Tools
{
    class ClassBuilder
    {
        public static object GetClassInstance(string className, Dictionary<string, Type> properties)
        {
            AppDomain myDomain = Thread.GetDomain();
            AssemblyName myAsmName = new AssemblyName();
            myAsmName.Name = "DynamicAssembly";

            AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.Run);
            ModuleBuilder myModBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name);
            TypeBuilder myTypeBuilder = myModBuilder.DefineType(className, TypeAttributes.Public);

            AddPropertyToTypeBuilder(myTypeBuilder, properties);

            Type retval = myTypeBuilder.CreateType();
            return Activator.CreateInstance(retval);
        }

        private static void AddPropertyToTypeBuilder(TypeBuilder myTypeBuilder, Dictionary<string, Type> properties)
        {
            MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
            foreach (var item in properties)
            {
                FieldBuilder fieldBuilder = myTypeBuilder.DefineField("__" + item.Key, item.Value, FieldAttributes.Private);

                PropertyBuilder propBldr = myTypeBuilder.DefineProperty(item.Key, PropertyAttributes.None, item.Value, null);

                // get
                MethodBuilder propGetMethod = myTypeBuilder.DefineMethod("get_" + item.Key, getSetAttr, item.Value, Type.EmptyTypes);
                ILGenerator propGetIL = propGetMethod.GetILGenerator();
                propGetIL.Emit(OpCodes.Ldarg_0);
                propGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
                propGetIL.Emit(OpCodes.Ret);

                // set
                MethodBuilder propSetMethod = myTypeBuilder.DefineMethod("set_" + item.Key, getSetAttr, null, new Type[] { item.Value });
                ILGenerator propSetIL = propSetMethod.GetILGenerator();
                propSetIL.Emit(OpCodes.Ldarg_0);
                propSetIL.Emit(OpCodes.Ldarg_1);
                propSetIL.Emit(OpCodes.Stfld, fieldBuilder);
                propSetIL.Emit(OpCodes.Ret);

                propBldr.SetSetMethod(propSetMethod);
                propBldr.SetGetMethod(propGetMethod);
            }
        }
    }
}
